package com.alotuser;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import com.alotuser.util.ResourcesUtil;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

@Mojo(name = "winsw", defaultPhase = LifecyclePhase.PACKAGE)
public class WindowsServiceMojo extends AbstractMojo {
    /**
     * 项目版本号
     */
    @Parameter(property = "project.version", required = true)
    private String version;

    /**
     * 项目artifactId
     */
    @Parameter(property = "project.artifactId", required = true, defaultValue = "")
    private String artifactId;
    /**
     * 项目描述
     */
    @Parameter(property = "project.description")
    private String description;
    /**
     * 打包类型
     */
    @Parameter(property = "project.packaging")
    private String packaging;
    /**
     * 输出目录
     */
    @Parameter(property = "project.build.directory", required = true)
    private File targetDir;
    /**
     * 扩展文件目录
     */
    @Parameter
    private File extend;

    /**
     * 启动参数 已过期
     * 推荐使用programArguments
     *
     * @deprecated
     */
    @Parameter(property = "arguments")
    private String[] arguments;
    /**
     * jvm 参数信息
     */
    @Parameter(property = "vmOptions")
    private String vmOptions;
    /**
     * 启动命令
     */
    @Parameter(property = "programExecutable", defaultValue = "java")
    private String programExecutable;
    /**
     * 启动参数
     */
    @Parameter(property = "programArguments")
    private String programArguments;

    /**
     * 是否需要版本号
     */
    @Parameter(property = "isVersion", defaultValue = "true")
    private boolean isVersion;
    /**
     * WinSW下载地址
     */
    @Parameter(property = "winUrl", defaultValue = "https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW-x64.exe")
    private String winUrl;
    /**
     * jdk版本号
     */
    @Parameter(property = "jdkVersion", defaultValue = "21")
    private String jdkVersion;

    public void execute() {
        getLog().info("开始生成 Windows Service 必要的文件");
        String tempDir = System.getProperty("java.io.tmpdir");
        tempDir += "sw";
//        String wv = WinPlatformUtil.getEqualsStrIgnoreCase(winPlatform, "x64", "x86", "net2", "net4", "net461");
//        if (null == wv) {
//            getLog().info("未找到winVersion" + winPlatform + "，例如:x64 x86 net2 net4 net461");
//            return;
//        }
//        wv = wv.toUpperCase();
//        getLog().info("winPlatform:" + wv);

        //https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW.NET2.exe
        //https://mirror.ghproxy.com/https://github.com/ngc660sec/NGCBot/releases/download/V2.1/WeChatSetup-3.9.10.27.exe
        //https://download.oracle.com/graalvm/17/latest/graalvm-jdk-17_windows-x64_bin.zip
        //https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.zip
        String fileName = FileUtil.getName(winUrl);
//        String url = "https://mirror.ghproxy.com/https://github.com/winsw/winsw/releases/download/";
//        url += winVersion + "/" + fileName;
        String jdkName = "jdk-" + jdkVersion + "_windows-x64_bin.zip";
        String jdkUrl = "https://download.oracle.com/java/" + jdkVersion + "/latest/" + jdkName;

        String resName = StrUtil.concat(true, tempDir, File.separator, fileName);
        String jdkPath = StrUtil.concat(true, tempDir, File.separator, jdkName);
        String jdkUnZipPath = StrUtil.concat(true, tempDir, File.separator, jdkVersion);
        String jdkTargetName = "jdk" + jdkVersion;
        programExecutable = jdkTargetName + File.separator + "bin" + File.separator + programExecutable;
        try {
            getLog().info("下载WinSW：" + winUrl);
            if (!FileUtil.exist(resName)) {
                HttpUtil.downloadFile(winUrl, resName);
            }
            getLog().info("下载WinSW成功");
            getLog().info("下载JDK：" + jdkUrl);
            if (!FileUtil.exist(jdkPath)) {
                HttpUtil.downloadFile(jdkUrl, jdkPath);
            }
            getLog().info("下载JDK成功");
        } catch (Exception e) {
            getLog().error("下载文件失败,版本不存在：" + winUrl, e);
            return;
        }

        try {
            File unzip = ZipUtil.unzip(jdkPath, jdkUnZipPath);

            for (File file : Objects.requireNonNull(unzip.listFiles())) {
                if (file.isDirectory()) {
                    File target = new File(jdkUnZipPath + File.separator + jdkTargetName);
                    file.renameTo(target);
                    jdkUnZipPath = target.getPath();
                    break;
                }
            }
            getLog().info("解压jdk文件到：" + jdkUnZipPath);
            String jarName = getJarName(isVersion), jarNames = getJarName(isVersion);
            if (!FileUtil.exist(targetDir.getPath() + File.separator + jarNames)) {
                jarNames = getJarName();
            }
            if (!FileUtil.exist(targetDir.getPath() + File.separator + jarNames)) {
                jarNames = getJarName(!isVersion);
            }
            if (FileUtil.exist(targetDir.getPath() + File.separator + jarNames)) {
                /* 创建文件夹 */
                File distDir = new File(targetDir, File.separator + "dist");
                if (distDir.exists()) {
                    try {
                        FileUtil.del(distDir);
                    } catch (Exception e) {
                        getLog().error("删除目录失败！请检查文件是否在使用");
                        e.printStackTrace();
                    }
                }
                FileUtil.mkdir(distDir.getPath());
                File logDir = new File(distDir, File.separator + "logs");
                FileUtil.mkdir(logDir.getPath());
                String jarPrefixName = getJarPrefixName(isVersion);

                /* 复制文件 */
                FileUtil.copy(new File(resName), new File(distDir, File.separator + jarPrefixName + ".exe"), true);
                FileUtil.writeString(format(jarName, jarPrefixName), new File(distDir, File.separator + jarPrefixName + ".xml"), CharsetUtil.UTF_8);
                FileUtil.writeString(ResourcesUtil.README_FILE, new File(distDir, File.separator + "readme.txt"), CharsetUtil.UTF_8);
                FileUtil.copy(new File(targetDir.getPath() + File.separator + jarNames), new File(distDir, File.separator + jarName), true);
                FileUtil.copy(new File(jdkUnZipPath), new File(distDir, File.separator), true);
                if (extend != null) {
                    FileUtil.copy(extend, new File(distDir, File.separator), true);
                }

                createBat(distDir, "install.bat", "install");
                createBat(distDir, "uninstall.bat", "uninstall");
                createBat(distDir, "start.bat", "start");
                createBat(distDir, "stop.bat", "stop");
                createBat(distDir, "restart.bat", "restart");

                getLog().info("正在制作压缩包....");
                String zipDir = targetDir.getPath() + File.separator + jarPrefixName + ".zip";
                ZipUtil.zip(distDir.getPath(), zipDir);
                getLog().info("正在清除临时文件....");
                FileUtil.del(distDir);
                getLog().info("制作完成，文件:" + zipDir);
            } else {
                getLog().info("制作Windows Service 失败:未找到文件");
            }

        } catch (Exception e) {
            getLog().error("制作Windows Service 失败：", e);
        }
    }

    /**
     * 属性转化
     *
     * @param jarName       jarName
     * @param jarPrefixName jarPrefixName
     * @return 格式化文本
     */
    private String format(String jarName, String jarPrefixName) {

        if (arguments.length > 0) {
            getLog().warn("arguments 参数设置已过期,参数配置可能不会生效,请分别设置 vmOptions 参数 和 programArguments 参数");
        }
        String vm_options = StrUtil.isEmpty(vmOptions) ? " " : " " + vmOptions + " ";
        String program_arguments = StrUtil.isEmpty(programArguments) ? "" : " " + programArguments;

        Map<String, Object> elements = new LinkedHashMap<String, Object>();

        elements.put("id", artifactId);
        elements.put("name", jarPrefixName);
        elements.put("description", null == description ? "暂无描述" : description);
        elements.put("envName", jarPrefixName.toUpperCase());
        elements.put("executable", programExecutable);
        elements.put("arguments", vm_options + "-jar " + jarName + program_arguments);

        return StrUtil.format(ResourcesUtil.XML_FILE, elements);

    }

    /**
     * @param outDri   输出目录
     * @param fileName 文件名
     * @param text     命令文本
     */
    private void createBat(File outDri, String fileName, String text) {
        if (!outDri.exists()) {
            FileUtil.mkdir(outDri.getPath());
        }
        File file = new File(outDri, fileName);
        try (FileWriter w = new FileWriter(file)) {
            w.write("@echo off\n" + "%1 mshta vbscript:CreateObject(\"Shell.Application\").ShellExecute(\"cmd.exe\",\"/c %~s0 ::\",\"\",\"runas\",1)(window.close)&&exit\n" + "%~dp0" + getJarPrefixName(isVersion) + ".exe " + text + "\n" + "echo The " + getJarPrefixName(isVersion) + " service current state:\n" + "%~dp0" + getJarPrefixName(isVersion) + ".exe status\n" + "pause");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取jar包前缀名
     *
     * @return String
     */
    private String getJarPrefixName() {
        return getJarPrefixName(true);
    }

    /**
     * 获取jar包前缀名
     *
     * @param isVersion
     * @return String
     */
    private String getJarPrefixName(boolean isVersion) {
        return artifactId + (isVersion ? "-" + version : "");
    }

    /**
     * 获取jar包全名
     *
     * @return String
     */
    private String getJarName() {
        return getJarPrefixName() + "." + packaging;
    }

    /**
     * 获取jar包全名
     *
     * @param isVersion
     * @return String
     */
    private String getJarName(boolean isVersion) {
        return getJarPrefixName(isVersion) + "." + packaging;
    }

}
